using System;
using System.Collections.Generic;
using System.Text;
using TTDotNet.Util;
using TTDotNet.Engine.Companies;
using TTDotNet.Engine.ThreeDEngine;
using TTDotNet.Engine.Stations;
using TTDotNet.Engine.Stations.Depot;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using TTDotNet.Engine.Networks;

namespace TTDotNet.Engine.Vehicles
{
    public class Vehicle : GameObject
    {
        public const float roadSpeedScale = 0.02f;

        protected bool isInService = true;

        private Company companyOwnedBy;
        protected VehicleInfo vehicleInfo;

        protected Dictionary<CargoType, List<CargoPacket>> currentLoad;
        
        protected float xSpeed, zSpeed; // MAKE A VECTOR2
        
        private DateTime dateBuilt;

        private OrderList orderList;
        protected int currentOrder;

        protected DDDCoordinate currentDestinationCoord;
        protected DDDCoordinate finalDestinationCoord;
        
        protected Vector3 currentDestination;
        protected Vector3 finalDestination;

        private bool isStopped;
        protected bool needNewInterimDestination;
        
        public Vehicle(string name, VehicleInfo info, Company company, DateTime builtDate, Vector3 location) 
            : base(location)
        {
            isStopped = true;
            companyOwnedBy = company;
            vehicleInfo = info;
            currentDestination = new Vector3(-1, -1, -1);
            xSpeed = 0; 
            zSpeed = 0;
            dateBuilt = builtDate;
            currentLoad = new Dictionary<CargoType, List<CargoPacket>>();

            foreach (CargoType type in vehicleInfo.CargoList)
            {
                currentLoad[type] = new List<CargoPacket>();
            }

            orderList = new OrderList();
            currentOrder = -1;

            objectInfo = new ObjectInfo(company, ObjectType.Vehicle, name);
            Mesh = Game.meshManager.CreateMesh(info.MeshPath, Location, new Vector3(0, 0, 0), ObjectType.Vehicle);
            Mesh.Mesh.ScaleBy(new Vector3(1, 0.9f, 1));
        }

        public override string ToString()
        {
            return objectInfo.Name + " is a " + objectInfo.ObjectType.ToString() + " owned by " + objectInfo.CompanyOwner.GetCompanyName();
        }

        public InstancedMesh IMesh
        {
            get { return objectMesh; }
        }

        public DDDCoordinate GetFinalDestination()
        {
            if (orderList.Count > 0 && currentOrder > -1)
            {
                return orderList.GetOrder(currentOrder).GetOrderCoord();
            }
            else
            {
                return new DDDCoordinate(-1, -1, -1);
            }
        }

        protected void ArrivedAtFinalDestination()
        {
            if (!(orderList.GetOrder(currentOrder).DestinationStation is Depot))
            {
                UnloadCargo();
                PickupCargo();
            }

            currentOrder = orderList.NextOrderNumber(currentOrder);
        }

        protected virtual void PickupCargo()
        {
            Station station = orderList.GetOrder(currentOrder).DestinationStation;
            List<List<CargoPacket>> delivery = new List<List<CargoPacket>>();

            foreach (CargoType type in currentLoad.Keys)
            {
                List<CargoPacket> received = station.SendOutCargo(type, vehicleInfo.GetCapacityOf(type) - CarryingAmount(type));
                if(received.Count > 0) delivery.Add(received);
            }

            foreach (List<CargoPacket> packetList in delivery)
            {
                foreach (CargoPacket packet in packetList)
                {
                    currentLoad[packet.CargoType].Add(packet);
                }
            }
        }

        private int CarryingAmount(CargoType type)
        {
            int amount = 0;

            foreach (CargoPacket packet in currentLoad[type])
            {
                amount += (int)packet.CargoAmount;
            }

            return amount;
        }

        protected virtual void UnloadCargo()
        {
            Station station = orderList.GetOrder(currentOrder).DestinationStation;
            List<CargoType> typesDelivered = new List<CargoType>();

            foreach (CargoType type in currentLoad.Keys)
            {
                if (station.Accepts.Contains(type))
                {
                    decimal moneyEarnt = station.ReceiveCargoFromVehicle(currentLoad[type]);
                    companyOwnedBy.AddFunds(moneyEarnt);

                    typesDelivered.Add(type);
                }
            }

            foreach (CargoType type in typesDelivered)
            {
                currentLoad[type].Clear();
            }

        }

        

        public void SetLocation(Vector3 loc) 
        {
            Location = loc;
            objectMesh.Mesh.MoveInstance(loc, objectMesh.GUID);
        }

        public void RotateBy(Vector3 rot)
        {
            Vector3 currentRotation = Rotation;

            currentRotation.X += rot.X;
            currentRotation.Y += rot.Y;
            currentRotation.Z += rot.Z;

            Rotation = currentRotation;

            //objectMesh.Mesh.RotateInstanceTo(objectRotation, objectMesh.GUID);
        }

        public void RotateTo(Vector3 rot)
        {
            Rotation = rot;
            //objectMesh.Mesh.RotateInstanceTo(rot, objectMesh.GUID);
        }

        public void AddOrder(Order o)
        {
            orderList.AddOrder(o);
            if (currentOrder == -1) currentOrder = 0;
        }

        public void AddOrderAt(Order o, int place)
        {
            orderList.AddOrderAt(o, place);
            if (currentOrder == -1) currentOrder = 0;
        }

        public new String Name
        {
            get { return objectInfo.Name;}
            set { objectInfo.Name = value; }
        }

        public bool NeedNewInterimDestination
        {
            get { return needNewInterimDestination; }
        }

        public virtual void DoMove(float fractionElapsed)
        {

        }

        public Company CompanyOwner
        {
            get { return companyOwnedBy; }
        }

        public bool IsStopped
        {
            get { return isStopped; }
            set { isStopped = value; }
        }

        public VehicleInfo VehicleInformation
        {
            get { return vehicleInfo; }
        }

        public OrderList Orders
        {
            get { return orderList; }
        }

        public string StatusText
        {
            get
            {
                if (IsStopped)
                {
                    return "Stopped.";
                }
                else
                {
                    return "Moving at " + vehicleInfo.MaxSpeed + "mph.";
                }
            }
        }

        public bool IsInService
        {
            get { return isInService; }
        }

    }
}
